﻿//RealTimeEmulationTrader.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Algo.Testing
{
	using System;
	using System.Collections.Generic;
	using System.Linq;

	using Ecng.Common;
	using Ecng.Serialization;
	using Ecng.Collections;

	using StockSharp.Algo.Storages;
	using StockSharp.Logging;
	using StockSharp.BusinessEntities;

	/// <summary>
	/// Тестовый шлюз, предназначенный для тестирования стратегии c реальном подключения к торговой системе через <see cref="UnderlyingTrader"/>,
	/// но без реального выставления заявок на бирже. Испонение заявок и их сделки эмулируются шлюзом, используя информацию по стаканам, приходящих от реального шлюза.
	/// </summary>
	/// <typeparam name="TUnderlyingTrader">Тип реального шлюза, с которым будет вестить тестирование.</typeparam>
	public class RealTimeEmulationTrader<TUnderlyingTrader> : BaseLogReceiver<ITrader>, ITrader
		where TUnderlyingTrader : class, ITrader
	{
		private readonly bool _ownTrader;

		/// <summary>
		/// Создать <see cref="RealTimeEmulationTrader{TUnderlyingTrader}"/>.
		/// </summary>
		public RealTimeEmulationTrader()
			: this(Activator.CreateInstance<TUnderlyingTrader>())
		{
		}

		/// <summary>
		/// Создать <see cref="RealTimeEmulationTrader{TUnderlyingTrader}"/>.
		/// </summary>
		/// <param name="underlyingTrader">Реальный шлюз к торговой системе.</param>
		/// <param name="ownTrader">Контролировать время жизни шлюза <paramref name="underlyingTrader"/>.</param>
		public RealTimeEmulationTrader(TUnderlyingTrader underlyingTrader, bool ownTrader = true)
		{
			if (underlyingTrader == null)
				throw new ArgumentNullException("underlyingTrader");

			TransactionIdGenerator = new IncrementTransactionIdGenerator();

			UnderlyingTrader = underlyingTrader;
			IsHistoryEmulation = underlyingTrader is DataFeedTrader;

			_ownTrader = ownTrader;
			MarketEmulator = new MarketEmulator(this);

			SubscribeEvents();
		}

		/// <summary>
		/// Реальный шлюз к торговой системе.
		/// </summary>
		public TUnderlyingTrader UnderlyingTrader { get; private set; }

		/// <summary>
		/// True, если тестирование производится на истории с помощью <see cref="DataFeedTrader"/>.
		/// </summary>
		public bool IsHistoryEmulation { get; private set; }

		private IMarketEmulator _marketEmulator;

		/// <summary>
		/// Эмулятор биржи.
		/// </summary>
		public IMarketEmulator MarketEmulator
		{
			get { return _marketEmulator; }
			set
			{
				if (value == null)
					throw new ArgumentNullException("value");

				if (_marketEmulator != null)
					DisposeEmulator();

				_marketEmulator = value;

				_marketEmulator.NewOrder += NewOrderHandler;
				_marketEmulator.OrderChanged += OrderChangedHandler;
				_marketEmulator.OrderFailed += OrderFailedHandler;
				_marketEmulator.NewMyTrades += NewMyTradesHandler;
				_marketEmulator.ProcessDataError += ProcessDataErrorHandler;
			}
		}

		/// <summary>
		/// Генератор идентификаторов транзакций.
		/// </summary>
		public TransactionIdGenerator TransactionIdGenerator { get; set; }

		#region NewPortfolios

		/// <summary>
		/// Событие появления новых портфелей.
		/// </summary>
		public event Action<IEnumerable<Portfolio>> NewPortfolios;

		private void NewPortfoliosHandler(IEnumerable<Portfolio> portfolios)
		{
			NewPortfolios.SafeInvoke(portfolios);
		}

		#endregion

		#region PortfoliosChanged

		/// <summary>
		/// Событие изменения параметров портфелей.
		/// </summary>
		public event Action<IEnumerable<Portfolio>> PortfoliosChanged;

		private void PortfoliosChangedHandler(IEnumerable<Portfolio> portfolios)
		{
			PortfoliosChanged.SafeInvoke(portfolios);
		}

		#endregion

		#region NewPositions

		/// <summary>
		/// Событие появления новых позиций.
		/// </summary>
		public event Action<IEnumerable<Position>> NewPositions;

		private void NewPositionsHandler(IEnumerable<Position> positions)
		{
			NewPositions.SafeInvoke(positions);
		}

		#endregion

		#region PositionsChanged

		/// <summary>
		/// Событие изменения параметров позиций.
		/// </summary>
		public event Action<IEnumerable<Position>> PositionsChanged;

		private void PositionsChangedHandler(IEnumerable<Position> positions)
		{
			PositionsChanged.SafeInvoke(positions);
		}

		#endregion

		#region NewSecurities

		/// <summary>
		/// Событие появления новых инструментов.
		/// </summary>
		public event Action<IEnumerable<Security>> NewSecurities;

		private void NewSecuritiesHandler(IEnumerable<Security> securities)
		{
			NewSecurities.SafeInvoke(securities);
		}

		#endregion

		#region SecuritiesChanged

		/// <summary>
		/// Событие изменения параметров инструментов.
		/// </summary>
		public event Action<IEnumerable<Security>> SecuritiesChanged;

		private void SecuritiesChangedHandler(IEnumerable<Security> securities)
		{
			SecuritiesChanged.SafeInvoke(securities);
		}

		#endregion

		#region NewTrades

		/// <summary>
		/// Событие появления всех новых сделок.
		/// </summary>
		public event Action<IEnumerable<Trade>> NewTrades;

		private void NewTradesHandler(IEnumerable<Trade> trades)
		{
			NewTrades.SafeInvoke(trades);
		}

		#endregion

		#region NewMyTrades

		/// <summary>
		/// Событие появления собственных новых сделок.
		/// </summary>
		public event Action<IEnumerable<MyTrade>> NewMyTrades;

		private void NewMyTradesHandler(IEnumerable<MyTrade> trades)
		{
			foreach(var trade in trades)
			{
				trade.Order = GetOrderByTransaction(trade.Order.TransactionId);
			}
			NewMyTrades.SafeInvoke(trades);
		}

		#endregion

		#region NewOrders

		/// <summary>
		/// Событие появления новых заявок.
		/// </summary>
		public event Action<IEnumerable<Order>> NewOrders;

		private void NewOrdersHandler(IEnumerable<Order> orders)
		{
			NewOrders.SafeInvoke(orders);
		}

		#endregion

		#region OrdersChanged

		/// <summary>
		/// Событие изменения состояния заявок (снята, удовлетворена).
		/// </summary>
		public event Action<IEnumerable<Order>> OrdersChanged;

		private void OrdersChangedHandler(IEnumerable<Order> orders)
		{
			OrdersChanged.SafeInvoke(orders);
		}

		#endregion

		#region OrdersRegisterFailed

		/// <summary>
		/// Событие об ошибках, связанных с регистрацией заявок.
		/// </summary>
		public event Action<IEnumerable<OrderFail>> OrdersRegisterFailed;

		private void OrdersRegisterFailedHandler(IEnumerable<OrderFail> fails)
		{
			OrdersRegisterFailed.SafeInvoke(fails);
		}

		#endregion

		#region OrdersCancelFailed

		/// <summary>
		/// Событие об ошибках, связанных со снятием заявок.
		/// </summary>
		public event Action<IEnumerable<OrderFail>> OrdersCancelFailed;

		private void OrdersCancelFailedHandler(IEnumerable<OrderFail> fails)
		{
			OrdersCancelFailed.SafeInvoke(fails);
		}

		#endregion

		#region NewStopOrders

		/// <summary>
		/// Событие появления новых стоп-заявок.
		/// </summary>
		public event Action<IEnumerable<Order>> NewStopOrders;

		private void NewStopOrdersHandler(IEnumerable<Order> orders)
		{
			NewStopOrders.SafeInvoke(orders);
		}

		#endregion

		#region StopOrdersChanged

		/// <summary>
		/// Событие появления новых стоп-заявок.
		/// </summary>
		public event Action<IEnumerable<Order>> StopOrdersChanged;

		private void StopOrdersChangedHandler(IEnumerable<Order> orders)
		{
			StopOrdersChanged.SafeInvoke(orders);
		}

		#endregion

		#region StopOrdersRegisterFailed

		/// <summary>
		/// Событие об ошибках, связанных с регистрацией стоп-заявок.
		/// </summary>
		public event Action<IEnumerable<OrderFail>> StopOrdersRegisterFailed;

		private void StopOrdersRegisterFailedHandler(IEnumerable<OrderFail> fails)
		{
			StopOrdersRegisterFailed.SafeInvoke(fails);
		}

		#endregion

		#region StopOrdersCancelFailed

		/// <summary>
		/// Событие об ошибках, связанных со снятием стоп-заявок.
		/// </summary>
		public event Action<IEnumerable<OrderFail>> StopOrdersCancelFailed;

		private void StopOrdersCancelFailedHandler(IEnumerable<OrderFail> fails)
		{
			StopOrdersCancelFailed.SafeInvoke(fails);
		}

		#endregion

		#region NewMarketDepths

		/// <summary>
		/// Событие появления новых стаканов с котировками.
		/// </summary>
		public event Action<IEnumerable<MarketDepth>> NewMarketDepths;

		private void NewMarketDepthsHandler(IEnumerable<MarketDepth> marketDepths)
		{
			NewMarketDepths.SafeInvoke(marketDepths);
		}

		#endregion

		#region MarketDepthsChanged

		/// <summary>
		/// Событие изменения стаканов с котировками.
		/// </summary>
		public event Action<IEnumerable<MarketDepth>> MarketDepthsChanged;

		private void MarketDepthsChangedHandler(IEnumerable<MarketDepth> marketDepths)
		{
			MarketDepthsChanged.SafeInvoke(marketDepths);

			if (IsHistoryEmulation)
				return;

			foreach (var md in marketDepths)
			{
				MarketEmulator.UpdateQuotes(md, null);
			}
		}

		#endregion

		#region NewOrderLogItems

		/// <summary>
		/// Событие появления новых записей в логе заявок.
		/// </summary>
		public event Action<IEnumerable<OrderLogItem>> NewOrderLogItems;

		private void NewOrderLogItemsHandler(IEnumerable<OrderLogItem> items)
		{
			NewOrderLogItems.SafeInvoke(items);
		}

		#endregion

		#region Connected

		/// <summary>
		/// Событие успешного подключения.
		/// </summary>
		public event Action Connected;

		private void ConnectedHandler()
		{
			Connected.SafeInvoke();
		}

		#endregion

		#region Disconnected

		/// <summary>
		/// Событие успешного отключения.
		/// </summary>
		public event Action Disconnected;

		private void DisconnectedHandler()
		{
			Disconnected.SafeInvoke();
		}

		#endregion

		#region ConnectionError

		/// <summary>
		/// Событие ошибки подключения (например, соединения было разорвано).
		/// </summary>
		public event Action<Exception> ConnectionError;

		private void ConnectionErrorHandler(Exception exception)
		{
			ConnectionError.SafeInvoke(exception);
		}

		#endregion

		#region NewDataExported

		/// <summary>
		/// Событие, сигнализирующее о новых экспортируемых данных.
		/// </summary>
		public event Action NewDataExported;

		private void NewDataExportedHandler()
		{
			NewDataExported.SafeInvoke();
		}

		#endregion

		#region ProcessDataError

		/// <summary>
		/// Событие, сигнализирующее об ошибке при получении или обработке новых данных с сервера.
		/// </summary>
		public event Action<Exception> ProcessDataError;

		private void ProcessDataErrorHandler(Exception exception)
		{
			ProcessDataError.SafeInvoke(exception);
		}

		#endregion

		#region MarketTimeChanged

		/// <summary>
		/// Событие, сигнализирующее об изменении текущего времени на биржевых площадках <see cref="ITrader.ExchangeBoards"/>.
		/// Передается разница во времени, прошедшее с последнего вызова события. Первый раз событие передает значение <see cref="TimeSpan.Zero"/>.
		/// </summary>
		public event Action<TimeSpan> MarketTimeChanged;

		private void MarketTimeChangedHandler(TimeSpan diff)
		{
			//MarketEmulator.Emulate(new TimeMessage(MarketEmulator.CurrentTime,MarketEmulator.CurrentTime));
			MarketTimeChanged.SafeInvoke(diff);
		}

		#endregion

		/// <summary>
		/// Текстовое описание шлюза.
		/// </summary>
		public string DisplayName
		{
			get { return UnderlyingTrader.DisplayName + " (эмуляция)"; }
		}

		/// <summary>
		/// Получить биржевое время.
		/// </summary>
		/// <param name="exchange">Биржа.</param>
		/// <returns>Биржевое время.</returns>
		public DateTime GetMarketTime(Exchange exchange)
		{
			return UnderlyingTrader.GetMarketTime(exchange);
		}

		/// <summary>
		/// Список всех биржевых площадок, для которых загружены инструменты <see cref="ITrader.Securities"/>.
		/// </summary>
		public IEnumerable<ExchangeBoard> ExchangeBoards
		{
			get { return UnderlyingTrader.ExchangeBoards; }
		}

		/// <summary>
		/// Список всех загруженных инструментов.
		/// Вызывать необходимо после того, как пришло событие <see cref="ITrader.NewSecurities" />. Иначе будет возвращено постое множество.
		/// </summary>
		public IEnumerable<Security> Securities
		{
			get { return UnderlyingTrader.Securities; }
		}

		/// <summary>
		/// Получить все заявки.
		/// </summary>
		public IEnumerable<Order> Orders
		{
			get { return UnderlyingTrader.Orders.Concat(MarketEmulator.Orders).ToArray(); }
		}

		/// <summary>
		/// Получить все стоп-заявки.
		/// </summary>
		public IEnumerable<Order> StopOrders
		{
			get { return UnderlyingTrader.StopOrders.Concat(MarketEmulator.Orders.Where(o => o.Type == OrderTypes.Conditional)).ToArray(); }
		}

		/// <summary>
		/// Получить все ошибки при регистрации заявок.
		/// </summary>
		public IEnumerable<OrderFail> OrderRegisterFails
		{
			get { return UnderlyingTrader.OrderRegisterFails.Concat(MarketEmulator.OrderRegisterFails).ToArray(); }
		}

		/// <summary>
		/// Получить все ошибки при снятии заявок.
		/// </summary>
		public IEnumerable<OrderFail> OrderCancelFails
		{
			get { return UnderlyingTrader.OrderCancelFails.Concat(MarketEmulator.OrderCancelFails).ToArray(); }
		}

		/// <summary>
		/// Получить все сделки.
		/// </summary>
		public IEnumerable<Trade> Trades
		{
			get { return UnderlyingTrader.Trades.Concat(MarketEmulator.MyTrades.Select(t => t.Trade)).ToArray(); }
		}

		/// <summary>
		/// Получить все собственные сделки.
		/// </summary>
		public IEnumerable<MyTrade> MyTrades
		{
			get { return UnderlyingTrader.MyTrades.Concat(MarketEmulator.MyTrades).ToArray(); }
		}

		/// <summary>
		/// Получить все портфели.
		/// </summary>
		public IEnumerable<Portfolio> Portfolios
		{
			get { return UnderlyingTrader.Portfolios; }
		}

		/// <summary>
		/// Получить все позиции.
		/// </summary>
		public IEnumerable<Position> Positions
		{
			get { return UnderlyingTrader.Positions; }
		}

		/// <summary>
		/// Проверить соединение.
		/// </summary>
		public bool IsConnected
		{
			get { return UnderlyingTrader.IsConnected; }
		}

		/// <summary>
		/// Запущен ли экспорт. Экспорт запускается через метод <see cref="ITrader.StartExport"/>.
		/// </summary>
		public bool IsExportStarted
		{
			get { return UnderlyingTrader.IsExportStarted; }
		}

		/// <summary>
		/// Поддерживается ли перерегистрация заявок через метод <see cref="ITrader.ReRegisterOrder(StockSharp.BusinessEntities.Order,StockSharp.BusinessEntities.Order)"/>
		/// в виде одной транзакции.
		/// </summary>
		public bool IsSupportAtomicReRegister
		{
			get
			{
				return MarketEmulator.Settings.IsSupportAtomicReRegister;
			}
		}

		/// <summary>
		/// Список всех инструментов, зарегистрированных через <see cref="ITrader.RegisterSecurity"/>.
		/// </summary>
		public IEnumerable<Security> RegisteredSecurities
		{
			get { return UnderlyingTrader.RegisteredSecurities; }
		}

		/// <summary>
		/// Список всех инструментов, зарегистрированных через <see cref="ITrader.RegisterMarketDepth"/>.
		/// </summary>
		public IEnumerable<Security> RegisteredMarketDepths
		{
			get { return UnderlyingTrader.RegisteredMarketDepths; }
		}

		/// <summary>
		/// Список всех инструментов, зарегистрированных через <see cref="ITrader.RegisterTrades"/>.
		/// </summary>
		public IEnumerable<Security> RegisteredTrades
		{
			get { return UnderlyingTrader.RegisteredTrades; }
		}

		/// <summary>
		/// Список всех инструментов, зарегистрированных через <see cref="ITrader.RegisterOrderLog"/>.
		/// </summary>
		public IEnumerable<Security> RegisteredOrderLogs
		{
			get { return UnderlyingTrader.RegisteredOrderLogs; }
		}

		/// <summary>
		/// Список всех портфелей, зарегистрированных через <see cref="ITrader.RegisterPortfolio"/>.
		/// </summary>
		public IEnumerable<Portfolio> RegisteredPortfolios
		{
			get { return UnderlyingTrader.RegisteredPortfolios; }
		}

		/// <summary>
		/// Подключиться к торговой системе.
		/// </summary>
		public void Connect()
		{
			UnderlyingTrader.Connect();
			MarketEmulator.Reset(LoggingHelper.Now);
			MarketEmulator.Start();
		}

		/// <summary>
		/// Отключиться от торговой системы.
		/// </summary>
		public void Disconnect()
		{
			MarketEmulator.Stop();
			UnderlyingTrader.Disconnect();
		}

		/// <summary>
		/// Переподключиться к торговой системе.
		/// </summary>
		public void Reconnect()
		{
			UnderlyingTrader.Reconnect();
		}

		/// <summary>
		/// Перезапустить экспорт данных из торговой системы через методы <see cref="ITrader.StopExport"/> и <see cref="ITrader.StartExport"/>.
		/// </summary>
		public void ReStartExport()
		{
			UnderlyingTrader.ReStartExport();
		}

		/// <summary>
		/// Получить позицию по портфелю и инструменту.
		/// </summary>
		/// <param name="portfolio">Портфель, по которому нужно найти позицию.</param>
		/// <param name="security">Инструмент, по которому нужно найти позицию.</param>
		/// <param name="depoName">Название депозитария, где находится физически ценная бумага.
		/// По-умолчанию передается пустая строка, что означает суммарную позицию по всем депозитариям.</param>
		/// <returns>Позиция.</returns>
		public Position GetPosition(Portfolio portfolio, Security security, string depoName = "")
		{
			return UnderlyingTrader.GetPosition(portfolio, security, depoName);
		}

		/// <summary>
		/// Получить стакан котировок.
		/// </summary>
		/// <param name="security">Инструмент, по которому нужно получить стакан.</param>
		/// <returns>Стакан котировок.</returns>
		public MarketDepth GetMarketDepth(Security security)
		{
			return UnderlyingTrader.GetMarketDepth(security);
		}

		/// <summary>
		/// Поддерживает ли шлюз инструмент.
		/// </summary>
		/// <param name="security">Инструмент.</param>
		/// <returns>True, если шлюз поддерживает инструмент, иначе, false.</returns>
		public bool IsSupport(Security security)
		{
			return UnderlyingTrader.IsSupport(security);
		}

		/// <summary>
		/// Начать получать новую информацию по портфелю.
		/// </summary>
		/// <param name="portfolio">Портфель, по которому необходимо начать получать новую информацию.</param>
		public void RegisterPortfolio(Portfolio portfolio)
		{
			UnderlyingTrader.RegisterPortfolio(portfolio);
		}

		/// <summary>
		/// Остановить получение новой информации по портфелю.
		/// </summary>
		/// <param name="portfolio">Портфель, по которому необходимо остановить получение новой информации.</param>
		public void UnRegisterPortfolio(Portfolio portfolio)
		{
			UnderlyingTrader.UnRegisterPortfolio(portfolio);
		}

		/// <summary>
		/// Начать получать лог заявок для инструмента.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо начать получать лог заявок.</param>
		public void RegisterOrderLog(Security security)
		{
			UnderlyingTrader.RegisterOrderLog(security);
		}

		/// <summary>
		/// Остановить получение лога заявок для инструмента.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо остановить получение лога заявок.</param>
		public void UnRegisterOrderLog(Security security)
		{
			UnderlyingTrader.UnRegisterOrderLog(security);
		}

		/// <summary>
		/// Начать получать новую информацию (например, <see cref="P:StockSharp.BusinessEntities.Security.LastTrade"/> или <see cref="P:StockSharp.BusinessEntities.Security.BestBid"/>) по инструменту.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо начать получать новую информацию.</param>
		public void RegisterSecurity(Security security)
		{
			UnderlyingTrader.RegisterSecurity(security);
		}

		/// <summary>
		/// Остановить получение новой информации.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо остановить получение новой информации.</param>
		public void UnRegisterSecurity(Security security)
		{
			UnderlyingTrader.UnRegisterSecurity(security);
		}

		/// <summary>
		/// Зарегистрировать заявку на бирже.
		/// </summary>
		/// <param name="order">Заявка, содержащая информацию для регистрации.</param>
		public void RegisterOrder(Order order)
		{
			order.CheckOnNew();

			order.InitOrder(this, TransactionIdGenerator);

			_byTransactionOrders[order.TransactionId] = order;

			MarketEmulator.RegisterOrder(order);
		}

		/// <summary>
		/// Перерегистрировать заявку на бирже.
		/// </summary>
		/// <param name="oldOrder">Заявка, которую нужно снять.</param>
		/// <param name="newOrder">Новая заявка, которую нужно зарегистрировать.</param>
		public void ReRegisterOrder(Order oldOrder, Order newOrder)
		{
			oldOrder.CheckOnOld();
			newOrder.CheckOnNew();

			newOrder.InitOrder(this, TransactionIdGenerator);

			_byTransactionOrders[newOrder.TransactionId] = newOrder;

			if (IsSupportAtomicReRegister)
				MarketEmulator.ReRegisterOrder(oldOrder, newOrder);
			else
			{
				MarketEmulator.CancelOrder(oldOrder);
				MarketEmulator.RegisterOrder(newOrder);
			}
		}

		/// <summary>
		/// Перерегистрировать заявку на бирже.
		/// </summary>
		/// <param name="oldOrder">Заявка, которую нужно снять и на основе нее зарегистрировать новую.</param>
		/// <param name="price">Цена новой заявки.</param>
		/// <param name="volume">Объем новой заявки.</param>
		/// <returns>Новая заявка.</returns>
		public Order ReRegisterOrder(Order oldOrder, decimal price, decimal volume)
		{
			oldOrder.CheckOnOld();

			var newOrder = oldOrder.ReRegisterClone(price, volume);
			ReRegisterOrder(oldOrder, newOrder);
			return newOrder;
		}

		/// <summary>
		/// Отменить заявку на бирже.
		/// </summary>
		/// <param name="order">Заявка, которую нужно отменять.</param>
		public void CancelOrder(Order order)
		{
			order.CheckOnOld();
			MarketEmulator.CancelOrder(order);
		}

		/// <summary>
		/// Отменить группу заявок на бирже по фильтру.
		/// </summary>
		/// <param name="isStopOrder">True, если нужно отменить только стоп-заявки, false - если только обычный и null - если оба типа.</param>
		/// <param name="portfolio">Портфель. Если значение равно null, то портфель не попадает в фильтр снятия заявок.</param>
		/// <param name="direction">Направление заявки. Если значение равно null, то направление не попадает в фильтр снятия заявок.</param>
		/// <param name="classCode">Код класса. Если переданная строка пустая, то код не попадает в фильтр снятия заявок.</param>
		/// <param name="security">Инструмент. Если значение равно null, то инструмент не попадает в фильтр снятия заявок.</param>
		public void CancelOrders(bool? isStopOrder, Portfolio portfolio, OrderDirections? direction, string classCode, Security security)
		{
			this.CancelOrders(MarketEmulator.Orders, isStopOrder, portfolio, direction, classCode, security);
		}

		/// <summary>
		/// Начать получать котировки (стакан) по инструменту.
		/// Значение котировок можно получить через метод <see cref="M:StockSharp.BusinessEntities.ITrader.GetMarketDepth(StockSharp.BusinessEntities.Security)"/>.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо начать получать котировки.</param>
		public void RegisterMarketDepth(Security security)
		{
			UnderlyingTrader.RegisterMarketDepth(security);
		}

		/// <summary>
		/// Остановить получение котировок по инструменту.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо остановить получение котировок.</param>
		public void UnRegisterMarketDepth(Security security)
		{
			UnderlyingTrader.UnRegisterMarketDepth(security);
		}

		/// <summary>
		/// Начать получать сделки (тиковые данные) по инструменту. Новые сделки будут приходить через событие <see cref="E:StockSharp.BusinessEntities.ITrader.NewTrades"/>.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо начать получать сделки.</param>
		public void RegisterTrades(Security security)
		{
			UnderlyingTrader.RegisterTrades(security);
		}

		/// <summary>
		/// Остановить получение сделок (тиковые данные) по инструменту.
		/// </summary>
		/// <param name="security">Инструмент, по которому необходимо остановить получение сделок.</param>
		public void UnRegisterTrades(Security security)
		{
			UnderlyingTrader.UnRegisterTrades(security);
		}

		/// <summary>
		/// Запустить экспорт данных из торговой системы в программу (получение портфелей, инструментов, заявок и т.д.).
		/// </summary>
		public void StartExport()
		{
			UnderlyingTrader.StartExport();
		}

		/// <summary>
		/// Остановить экспорт данных из торговой системы в программу, запущенный через <see cref="ITrader.StartExport"/>.
		/// </summary>
		public void StopExport()
		{
			UnderlyingTrader.StopExport();
		}

		private readonly SynchronizedDictionary<long, Order> _byTransactionOrders = new SynchronizedDictionary<long,Order>();

		private Order GetOrderByTransaction(long transactionId)
		{
			var order = _byTransactionOrders.TryGetValue(transactionId);

			if(order == null)
				throw new InvalidOperationException("Ордер с transactionId="+transactionId+" не найден");

			return order;
		}

		private void OrderFailedHandler(OrderFail fail)
		{
			var order = GetOrderByTransaction(fail.Order.TransactionId);
			
			using (order.BeginUpdate())
			{
				order.Id = fail.Order.Id;
				order.Time = fail.Order.Time;
				order.LastChangeTime = fail.Order.LastChangeTime;
				order.Balance = fail.Order.Balance;
				order.State = fail.Order.State;
			}

			if (order.Type == OrderTypes.Conditional)
			{
				if (order.Id == 0)
					StopOrdersRegisterFailedHandler(new[] { fail });
				else
					StopOrdersCancelFailedHandler(new[] { fail });
			}
			else
			{
				if (order.Id == 0)
					OrdersRegisterFailedHandler(new[] { fail });
				else
					OrdersCancelFailedHandler(new[] { fail });
			}
		}

		private void OrderChangedHandler(Order state)
		{
			var order = GetOrderByTransaction(state.TransactionId);
			using (order.BeginUpdate())
			{
				order.Id = state.Id;
				order.LastChangeTime = state.LastChangeTime;
				order.Time = state.Time;
				order.Balance = state.Balance;
				order.State = state.State;
			}
			if (order.Type == OrderTypes.Conditional)
				StopOrdersChangedHandler(new[] { order });
			else
				OrdersChangedHandler(new[] { order });
		}

		private void NewOrderHandler(Order state)
		{
			var order = GetOrderByTransaction(state.TransactionId);
			using (order.BeginUpdate())
			{
				order.Id = state.Id;
				order.LastChangeTime = state.LastChangeTime;
				order.Time = state.Time;
				order.Balance = state.Balance;
				order.State = state.State;
			}

			if (order.Type == OrderTypes.Conditional)
				NewStopOrdersHandler(new[] { order });
			else
				NewOrdersHandler(new[] { order });
		}

		/// <summary>
		/// Загрузить настройки.
		/// </summary>
		/// <param name="storage">Хранилище настроек.</param>
		public void Load(SettingsStorage storage)
		{
			UnderlyingTrader.Load(storage.GetValue<SettingsStorage>("trader"));
		}

		/// <summary>
		/// Сохранить настройки.
		/// </summary>
		/// <param name="storage">Хранилище настроек.</param>
		public void Save(SettingsStorage storage)
		{
			storage.SetValue("trader", UnderlyingTrader.Save());
		}

		private void DisposeEmulator()
		{
			MarketEmulator.NewOrder -= NewOrderHandler;
			MarketEmulator.OrderChanged -= OrderChangedHandler;
			MarketEmulator.OrderFailed -= OrderFailedHandler;
			MarketEmulator.NewMyTrades -= NewMyTradesHandler;
			MarketEmulator.ProcessDataError -= ProcessDataErrorHandler;

			MarketEmulator.Dispose();
		}

		/// <summary>
		/// Освободить занятые ресурсы.
		/// </summary>
		protected override void DisposeManaged()
		{
			UnSubscribeEvents();
			DisposeEmulator();

			if (_ownTrader)
				UnderlyingTrader.Dispose();

			base.DisposeManaged();
		}

		private void SubscribeEvents()
		{
			var dataFeed = UnderlyingTrader as DataFeedTrader;
			if (dataFeed != null)
				dataFeed.NewMessage += MarketEmulator.Emulate;

			UnderlyingTrader.NewSecurities += NewSecuritiesHandler;
			UnderlyingTrader.SecuritiesChanged += SecuritiesChangedHandler;
			UnderlyingTrader.NewPortfolios += NewPortfoliosHandler;
			UnderlyingTrader.PortfoliosChanged += PortfoliosChangedHandler;
			UnderlyingTrader.NewPositions += NewPositionsHandler;
			UnderlyingTrader.PositionsChanged += PositionsChangedHandler;
			UnderlyingTrader.NewTrades += NewTradesHandler;
			UnderlyingTrader.NewMyTrades += NewMyTradesHandler;
			UnderlyingTrader.NewOrders += NewOrdersHandler;
			UnderlyingTrader.OrdersChanged += OrdersChangedHandler;
			UnderlyingTrader.OrdersRegisterFailed += OrdersRegisterFailedHandler;
			UnderlyingTrader.OrdersCancelFailed += OrdersCancelFailedHandler;
			UnderlyingTrader.NewStopOrders += NewStopOrdersHandler;
			UnderlyingTrader.StopOrdersChanged += StopOrdersChangedHandler;
			UnderlyingTrader.StopOrdersRegisterFailed += StopOrdersRegisterFailedHandler;
			UnderlyingTrader.StopOrdersCancelFailed += StopOrdersCancelFailedHandler;
			UnderlyingTrader.NewMarketDepths += NewMarketDepthsHandler;
			UnderlyingTrader.MarketDepthsChanged += MarketDepthsChangedHandler;
			UnderlyingTrader.NewOrderLogItems += NewOrderLogItemsHandler;
			UnderlyingTrader.Connected += ConnectedHandler;
			UnderlyingTrader.Disconnected += DisconnectedHandler;
			UnderlyingTrader.ConnectionError += ConnectionErrorHandler;
			UnderlyingTrader.NewDataExported += NewDataExportedHandler;
			UnderlyingTrader.ProcessDataError += ProcessDataErrorHandler;
			UnderlyingTrader.MarketTimeChanged += MarketTimeChangedHandler;
		}

		private void UnSubscribeEvents()
		{
			var dataFeed = UnderlyingTrader as DataFeedTrader;
			if (dataFeed != null)
				dataFeed.NewMessage -= MarketEmulator.Emulate;

			UnderlyingTrader.NewSecurities -= NewSecuritiesHandler;
			UnderlyingTrader.SecuritiesChanged -= SecuritiesChangedHandler;
			UnderlyingTrader.NewPortfolios -= NewPortfoliosHandler;
			UnderlyingTrader.PortfoliosChanged -= PortfoliosChangedHandler;
			UnderlyingTrader.NewPositions -= NewPositionsHandler;
			UnderlyingTrader.PositionsChanged -= PositionsChangedHandler;
			UnderlyingTrader.NewTrades -= NewTradesHandler;
			UnderlyingTrader.NewMyTrades -= NewMyTradesHandler;
			UnderlyingTrader.NewOrders -= NewOrdersHandler;
			UnderlyingTrader.OrdersChanged -= OrdersChangedHandler;
			UnderlyingTrader.OrdersRegisterFailed -= OrdersRegisterFailedHandler;
			UnderlyingTrader.OrdersCancelFailed -= OrdersCancelFailedHandler;
			UnderlyingTrader.NewStopOrders -= NewStopOrdersHandler;
			UnderlyingTrader.StopOrdersChanged -= StopOrdersChangedHandler;
			UnderlyingTrader.StopOrdersRegisterFailed -= StopOrdersRegisterFailedHandler;
			UnderlyingTrader.StopOrdersCancelFailed -= StopOrdersCancelFailedHandler;
			UnderlyingTrader.NewMarketDepths -= NewMarketDepthsHandler;
			UnderlyingTrader.MarketDepthsChanged -= MarketDepthsChangedHandler;
			UnderlyingTrader.NewOrderLogItems -= NewOrderLogItemsHandler;
			UnderlyingTrader.Connected -= ConnectedHandler;
			UnderlyingTrader.Disconnected -= DisconnectedHandler;
			UnderlyingTrader.ConnectionError -= ConnectionErrorHandler;
			UnderlyingTrader.NewDataExported -= NewDataExportedHandler;
			UnderlyingTrader.ProcessDataError -= ProcessDataErrorHandler;
			UnderlyingTrader.MarketTimeChanged -= MarketTimeChangedHandler;
		}
	}

	/// <summary>
	/// Тестовый шлюз, предназначенный для тестирования стратегии c реальном подключения к торговой системе через <see cref="RealTimeEmulationTrader{TUnderlyingTrader}.UnderlyingTrader"/>,
	/// но без реального выставления заявок на бирже. Испонение заявок и их сделки эмулируются шлюзом, используя информацию по стаканам, приходящих от реального шлюза.
	/// </summary>
	public class RealTimeEmulationTrader : RealTimeEmulationTrader<ITrader>
	{
		/// <summary>
		/// Создать <see cref="RealTimeEmulationTrader"/>.
		/// </summary>
		/// <param name="underlyingTrader">Реальный шлюз к торговой системе.</param>
		public RealTimeEmulationTrader(ITrader underlyingTrader)
			: base(underlyingTrader)
		{
		}
	}
}
